package com.yangming.springboot;

import com.alibaba.fastjson.JSON;
import com.yangming.springboot.base.ResultInfo;
import com.yangming.springboot.exceptions.AuthException;
import com.yangming.springboot.exceptions.ParamsException;
import com.yangming.springboot.exceptions.NoLoginException;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

//globalExceptionresolver作为一个单例对象，注入IOC中，通过容器调用这个类，防止类被重复创建实例带来的无效内存消耗问题
@Component
public class GlobalExceptionResolver implements HandlerExceptionResolver {

    //这个方法一般就是处理控制层下所抛出的全局异常
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex){
        /*
        * 方法返回值有两种可能
        * 1.前后端不分离的话，要返回404页面视图内容
        * 2.json
        * 处理这两种情况，如果控制层方法配置了@ResponseBody这个配置项，就返回JSON，其他返回html页面(教程里面前后端不分离的，自己写的话，应该要返回别的东西)
        * */
        ModelAndView mv = new ModelAndView();
        mv.setViewName("error");
        mv.addObject("code", 400); //给code和msg一个默认值
        mv.addObject("msg", "system abnornal");

        /*
        * 检测错误类型是否是用户未登陆类型，是的话就不用执行下面的参数错误判断了，直接页面跳转至no_login页面，这是前后端不分离的写法，但是有其他类型的全局错误处理也可以这么干
        * */
        if (ex instanceof NoLoginException){
            mv.setViewName("no_login");
            mv.addObject("msg", "user no login");
            return mv;
        }



        if (handler instanceof HandlerMethod){
            HandlerMethod hm = (HandlerMethod)handler;
            ResponseBody responseBody = hm.getMethod().getDeclaredAnnotation(ResponseBody.class);
            if (responseBody == null){
                //说明这个抛出异常的方法没有配置ResponseBody这个配置注解
                if (ex instanceof ParamsException){
                    ParamsException pe = (ParamsException) ex;
                    mv.addObject("msg", pe.getMsg());
                    mv.addObject("code", pe.getCode());
                }else if (ex instanceof AuthException){
                    AuthException ae = (AuthException) ex;
                    mv.addObject("msg", ae.getMsg());
                    mv.addObject("code", ae.getCode());
                }


                return mv;
            }else{
                //反之就是配置了响应内容为JSON的方法
                ResultInfo resultInfo = new ResultInfo();
                resultInfo.setCode(300); //配置默认错误信息，如果有具体的错误信息，就set详细的错误code和msg
                resultInfo.setMsg("system error");

                if (ex instanceof ParamsException){
                    ParamsException pe = (ParamsException) ex;
                    resultInfo.setCode(pe.getCode());
                    resultInfo.setMsg(pe.getMsg());
                }else if (ex instanceof AuthException){
                    AuthException ae = (AuthException) ex;
                    resultInfo.setCode(ae.getCode());
                    resultInfo.setMsg(ae.getMsg());
                }
                //往响应体里写返回信息流，下面是配置响应头
                response.setContentType("application/json;charset=utf-8");
                response.setCharacterEncoding("utf-8");

                PrintWriter pw = null;
                try{
                    pw = response.getWriter();
                    //获取打印流，然后往流里写json内容，我们的resultinfo是一个类不是json，在这里需要用插件来把类进行转换
                    //这个插件就是alibaba的fastjson
                    pw.write(JSON.toJSONString(resultInfo));
                    pw.flush();
                }catch(Exception e){
                    e.printStackTrace();
                }finally{
                    if (pw != null){
                        pw.close();
                    }
                }
            }
        }else{
           return mv;
        }
        return mv;
    }

}
